package com.young.testcoroutine

import kotlinx.coroutines.*
import org.junit.Test

/**
 * Author：young
 * .
 * Time： 2022/9/13 21:42 周二
 * .
 * Desc：协程的取消
 */
class CoroutineTest05 {


    /**
     * 通过协程作用域取消
     */
    @Test
    fun testScopeCancel() = runBlocking<Unit> {
        /*
            CoroutineScope 和 coroutineScope {  } 的区别
            coroutineScope {  } 是协程构建器，里面自带 CoroutineScope
         */

        val scope = CoroutineScope(Dispatchers.Default)

        scope.launch {
            delay(1000)
            println("Job 1")
        }

        scope.launch {
            delay(1000)
            println("job 2")
        }

        delay(100)
        scope.cancel()

        // 这里如果不加，则没有任何输出
        // 因为 runBlocking 和 CoroutineScope 它们已经不是同一个上下文了
        delay(2000)

    }


    /**
     * 被取消的子协程并不会影响其余兄弟协程
     */
    @Test
    fun testBrotherCancel() = runBlocking<Unit> {
        val scope = CoroutineScope(Dispatchers.Default)

        val job1 = scope.launch {
            delay(1000)
            println("job 1")
        }

        val job2 = scope.launch {
            delay(1000)
            println("job 2")
        }

        delay(100)
        // job1 被取消的时候，job 2 是不受影响的
        job1.cancel()

        delay(2000)

    }


    /**
     * 协程通过抛出一个特殊的异常CancellationException来处理取消操作
     *
     * CancellationException 会被静默处理掉（就是一直往上抛），我们可以使用 catch 来捕获
     */
    @Test
    fun testCancellationException() = runBlocking<Unit> {
        val job = GlobalScope.launch {
            try {
                delay(1000)
                println("Job one")
            } catch (e: CancellationException) {
                println("取消的异常 ${e.message}")
            }
        }

        delay(100)
        job.cancel()
        job.join()

    }


    /**
     * 协程通过抛出一个特殊的异常CancellationException来处理取消操作
     *
     * CancellationException 会被静默处理掉（就是一直往上抛），我们可以使用 catch 来捕获
     */
    @Test
    fun testCancellationException1() = runBlocking<Unit> {
        val job = GlobalScope.launch {
            try {
                delay(1000)
                println("Job one")
            } catch (e: CancellationException) {
                println("取消的异常 ${e.message}")
            }
        }

        delay(100)
//        job.cancel(CancellationException("取消啊"))
//        job.join()
        // 上面两句可以合并为下面这句
        job.cancelAndJoin()

    }

    /**
     * cup 密集型任务取消（没办法取消，因为计算密集，如果忽然断掉，有些数据会丢失，所以cpu密集型任务是无法常规化取消的）
     */
    @Test
    fun test_cancel_cpu_task_by_isActive() = runBlocking<Unit> {
        var startTime = System.currentTimeMillis()
        val job = launch(Dispatchers.Default) {
            var i = 0
            while (i < 6) {
                if (System.currentTimeMillis() >= startTime) {
                    println("startTime = $startTime")
                    startTime += 600
                    i++
                }
            }
        }
        delay(1300)
        println("准备取消")
        job.cancelAndJoin()
        println("已经取消")
    }


    /**
     * cup 密集型任务取消（借助 isActive 进行取消）
     */
    @Test
    fun test_cancel_cpu_task_by_isActive1() = runBlocking<Unit> {
        var startTime = System.currentTimeMillis()
        val job = launch(Dispatchers.Default) {
            var i = 0
            while (i < 6 && isActive) {
                if (System.currentTimeMillis() >= startTime) {
                    println("startTime = $startTime")
                    startTime += 600
                    i++
                }
            }
        }
        delay(1300)
        println("准备取消")
        job.cancelAndJoin()
        println("已经取消")
    }

    /**
     * cup 密集型任务取消（借助 ensureActive() 进行取消）
     *
     * ensureActive() 会通过抛出 CancellationException 来取消协程
     */
    @Test
    fun test_cancel_cpu_task_by_isActive2() = runBlocking<Unit> {
        var startTime = System.currentTimeMillis()
        val job = launch(Dispatchers.Default) {
            var i = 0
            while (i < 6) {
                ensureActive()
                if (System.currentTimeMillis() >= startTime) {
                    println("startTime = $startTime")
                    startTime += 600
                    i++
                }
            }
        }
        delay(1300)
        println("准备取消")
        job.cancelAndJoin()
        println("已经取消")
    }

    /**
     * cup 密集型任务取消（借助 yield() 进行取消）
     * yield() 会通过抛出 CancellationException 来取消协程
     * 同时也会出让线程的执行权
     *
     * 因为 cpu 密集型，有可能高度密集，如果太密集，会导致cpu无法执行其他任务，所以使用 yield()
     * 出让执行权，让其他的任务也有机会执行
     */
    @Test
    fun test_cancel_cpu_task_by_isActive3() = runBlocking<Unit> {
        var startTime = System.currentTimeMillis()
        val job = launch(Dispatchers.Default) {
            var i = 0
            while (i < 6) {
                yield()
                if (System.currentTimeMillis() >= startTime) {
                    println("startTime = $startTime")
                    startTime += 600
                    i++
                }
            }
        }
        delay(1300)
        println("准备取消")
        job.cancelAndJoin()
        println("已经取消")
    }

}